package cn.uc.ucgc.community.upgrade.conf.task;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.uc.ucgc.community.upgrade.Constants;
import cn.uc.ucgc.community.upgrade.Task;
import cn.uc.ucgc.community.upgrade.Utils;
import cn.uc.ucgc.community.upgrade.conf.JobType;
import cn.uc.ucgc.community.upgrade.conf.ReplaceJob;
import cn.uc.ucgc.community.upgrade.conf.ReplaceJob.ReplaceItem;

/**
 * 字段替换
 * 
 * @author yuyj@ucweb.com
 * @createDate 2014-3-13
 *
 */
public class ReplaceTask extends Task {
    private static final Logger logger = LoggerFactory.getLogger(ReplaceTask.class);
    //需要替换的数据
    private List<ReplaceJob> jobs;

    public ReplaceTask(List<ReplaceJob> jobs) {
        super.setTaskDesc("Replace File Task");
        this.jobs = jobs;
    }

    @Override
    public void doTask(StringBuilder sb) throws Exception {
        if (null == jobs || jobs.isEmpty()) {
            sb.append(Constants.TASK_RESULT_NOTASK_SUFFIX);
            return;
        }
        //首先将XML/prop任务分开
        List<ReplaceJob> propJobs = new ArrayList<ReplaceJob>();
        List<ReplaceJob> xmlJobs = new ArrayList<ReplaceJob>();
        for (ReplaceJob job : jobs) {
            if (null == job) {
                continue;
            }
            if (JobType.PROP == job.getType()) {
                propJobs.add(job);
            } else {
                xmlJobs.add(job);
            }
        }
        logger.warn("Need replace prop file count:{}", propJobs.size());
        //对prop类型的做替换处理
        doPropJobs(propJobs);
        logger.warn("Need replace xml file count:{}", xmlJobs.size());
        //对xml类型的做替换处理
        doXmlJobs(xmlJobs);
    }

    private void doPropJobs(List<ReplaceJob> propJobs) throws IOException {
        for (ReplaceJob job : propJobs) {
            //外部要确保对应的源和目标文件都存在
            File srcPath = job.getSrcPath();
            File dstPath = job.getDstPath();
            logger.info("prop replace:{} to {}", srcPath.getCanonicalPath(),
                    dstPath.getCanonicalPath());
            List<ReplaceItem> replaceItems = job.getReplaceItems();
            if (null == replaceItems || replaceItems.isEmpty()) {
                logger.warn("no replace items");
                continue;
            }
            //从原文件中读取
            InputStream srcIS = null;
            InputStream dstIS = null;
            Map<String, String> replaceProps = new HashMap<String, String>();
            try {
                srcIS = new FileInputStream(srcPath);
                dstIS = new FileInputStream(dstPath);
                Properties srcProp = new Properties();
                srcProp.load(srcIS);
                //目标文件，也需要先读取出来，然后再替换
                Properties dstProp = new Properties();
                dstProp.load(dstIS);
                //依次替换对应的值
                for (ReplaceItem item : replaceItems) {
                    //初始化任务时，保证了对应的key非""/null
                    //两个key一定要都能找到
                    String srcKey = item.getSrcKey();
                    String dstKey = item.getDstKey();
                    String srcVal = srcProp.getProperty(srcKey);
                    //判断是否存在，不存在直接继续后续处理
                    if (null == srcVal) {
                        logger.warn("no srcKey:{}", srcKey);
                        continue;
                    }
                    String dstVal = dstProp.getProperty(dstKey);
                    //
                    if (null == dstVal) {
                        logger.warn("no dstKey:{}", dstKey);
                        continue;
                    }
                    //将对应的值做替换
                    replaceProps.put(dstKey, srcVal);
                }
            } finally {
                Utils.closeFileStream(srcIS);
                Utils.closeFileStream(dstIS);
            }
            //根据收集的数据，重写目标文件
            Utils.modifyPropFile(dstPath, replaceProps);
        }
    }

    private void doXmlJobs(List<ReplaceJob> xmlJobs) throws IOException, DocumentException {
        for (ReplaceJob job : xmlJobs) {
            //外部要确保对应的源和目标文件都存在
            File srcPath = job.getSrcPath();
            File dstPath = job.getDstPath();
            logger.info("xml replace:{} to {}", srcPath.getCanonicalPath(),
                    dstPath.getCanonicalPath());
            List<ReplaceItem> replaceItems = job.getReplaceItems();
            if (null == replaceItems || replaceItems.isEmpty()) {
                logger.warn("no replace items");
                continue;
            }
            //首先要读取出数据
            SAXReader srcReader = new SAXReader();
            SAXReader dstReader = new SAXReader();
            Document srcDom = srcReader.read(srcPath);
            Document dstDom = dstReader.read(dstPath);
            for (ReplaceItem item : replaceItems) {
                //首先要从源xml文件中获取对应的数据，这里是直接定义成dom4j的xPath格式
                String srcKeyPath = item.getSrcKey();
                Node srcVal = srcDom.selectSingleNode(srcKeyPath);
                if (null == srcVal) {
                    logger.warn("no srcKeyPath:{}", srcKeyPath);
                    continue;
                }
                String dstKeyPath = item.getDstKey();
                Node dstVal = dstDom.selectSingleNode(dstKeyPath);
                if (null == dstVal) {
                    logger.warn("no dstKeyPath:{}", dstKeyPath);
                    continue;
                }
                //这里直接操作目标的dom
                Element srcEle = (Element) srcVal;
                Element dstEle = (Element) dstVal;
                boolean bLeafNode = srcEle.isTextOnly();
                //如果是叶子节点，则目标必须也是叶子节点
                if (bLeafNode) {
                    if (!dstEle.isTextOnly()) {
                        //无法直接替换，打印日志，继续下一个
                        logger.warn("dstEle:{} not a leaf node", dstKeyPath);
                        continue;
                    }
                    //直接将文本替换过去
                    dstEle.setText(srcEle.getText());
                } else {
                    //这个就是子结构替换了
                    Element parent = dstEle.getParent();
                    parent.remove(dstEle);
                    parent.add((Element) srcEle.clone());
                }
            }
            //至此，目标dom已经更新完了，需要写如文件
            Utils.saveDom2Xml(dstDom, dstPath);
        }
    }
}
